<?php
namespace Core;
if ( ! defined('BASEPATH')) exit('No direct script access allowed');

/*
** etc:  mysql操作类
** author: deepe.xiao
** update: 2018-10-10
*/



class MyPDO{
	private $host;
	private $port;
	private $charset;
	private $pdo;//数据库对象
	private $dbname;

	private $transTimes;//同一时间事物的数量
	private static $mysql;
	static $_instance = array() ;

	private function __construct(){
		$config = include_once( FCPATH."/app/config/db.php" );

		if((!$config['host']) || (!$config['port']) || (!$config['user']) || (!$config['database'])) {
			view_log(
			   'Db Config Error',
			   'Please check your config array , make sure array include these config item:
			   <font color="red">
			   <br/>$config["host"]
			   <br/>$config["port"]
			   <br/>$config["user"]
			   <br/>$config["passwd"]
			   <br/>$config["database"]
			   </font>
			   '
			);
		}
		
		$this->host = $config['host'];
		$this->port = $config['port'];
		$this->user = $config['user'];
		$this->passwd = $config['passwd'];
		$this->database = $config['database'];
		$this->dbname = $config['database'];
		$this->charset = $GLOBALS['config']['dcharset'];

		try{
			//pdo连接数据库
			$this->pdo = new \PDO("mysql:host=$this->host;dbname=$this->database", "$this->user", "$this->passwd");
			//发送编码
			$this->pdo->query("set names $this->charset");
		}catch (\PDOException $e){
			die(view_log("Db Connect Error","Can't connect to mysql server!"));
		}
	}

	private function __clone(){}

	//单件模式
	static function getInstance()  {
		if( self::$mysql ==  null ){
			$class = __CLASS__;
//			self::$mysql = new $class();
			self::$mysql =   $instance = $_instance[$class] = new static();
			return $instance;
		}
		return self::$mysql;
	}


	/**
	 * 定义执行查询sql语句的方法
	 * 参数： 查询sql语句
	 * 返回： 二维关联数组
	 */
	public function fetch_sql($sql)
	{
		$res = $this->pdo->query($sql)or die( view_log( 'MySQL Query Error', $this->pdo->errorInfo() ));
		$res->setFetchMode(\PDO::FETCH_ASSOC);
		$arr = $res->fetchAll();
		return $arr;
	}

	/**
	 * 增删改 的简单方法
	 * @param $sql
	 * @return int
	 */
	public function exec_sql($sql){
		$result = $this->pdo->exec($sql)or die( view_log( 'MySQL exec Error', $this->pdo->errorInfo() ));
		return $result;
	}



	/**
	 * 查询一行记录的方法
	 * 参数：表名 条件(不包含where)
	 * 返回：一维关联数组
	 */
	public function getRow($sql)
	{
		//查询
		$res = $this->pdo->query($sql)or die( view_log( 'MySQL Query Error', $this->pdo->errorInfo() ));
		if ($res){
			$res->setFetchMode(\PDO::FETCH_ASSOC);
			$arr = $res->fetch();
			return $arr;
		}
		return array();

	}






	/*****    以下方法高级用法   ****/


	/**
	 * 查询某个字段
	 * 参数： 字段名(多个的话用逗号隔开) 表名 条件（不含where）
	 * 返回： 二维关联数组
	 */
	public function getOne($column, $tablename, $where = "1")
	{
//拼接sql语句
		$sql = "select $column from $tablename where $where";
		$rs = $this->pdo->query($sql);
		$rs->setFetchMode(\PDO::FETCH_ASSOC);
//$col = $rs->fetchColumn();
		$col = $rs->fetchAll();
		return $col;
	}



	/**
	 * 向数据库中添加一条信息
	 * 参数：表名 一维关联数组
	 * 返回: 布尔值
	 */
	public function insert($tablename, $arr)
	{
//拿到数组之后先处理数组 过滤字段
//取出表中的字段
		$sql = "select COLUMN_NAME from information_schema.COLUMNS where table_name = '$tablename' and table_schema ='$this->dbname'";
		$columns = $this->pdo->query($sql);
		$columns->setFetchMode(\PDO::FETCH_ASSOC);
		$columns = $columns->fetchAll();
		$cols = array(); //存储表中的全部字段
		foreach ($columns as $key => $val) {
			$cols[] = $val['COLUMN_NAME'];
		}
//将要入库的数组进行键值分离
		$keys = array();
		$values = '';
		foreach ($arr as $k => $v) {
			if (!in_array($k, $cols)) {
				unset($arr[$k]);
			} else {
				$keys[] = $k;
				$values .= "'" . $v . "',";
			}
		}
		$column = implode(',', $keys);
		$values = substr($values, 0, -1);
//拼接sql语句
		$sql = "insert into $tablename($column) values ($values)";
		echo $sql;
		$res = $this->pdo->exec($sql);
		var_dump($res);
		return $res;
	}


	/**
	 * 删除数据 其实就是改变字段值使之不被查询
	 * 参数：表名 条件(不含where)
	 * 返回：布尔
	 */
	public function delete($tablename, $where)
	{
		$sql = "update $tablename set is_del=1 where $where";
		$res = $this->pdo->exec($sql);
		return $res;
	}
	/**
	 * 谨慎操作,真实删除数据
	 * 参数：表名 条件(不含where)
	 * 返回：布尔
	 */
	public function remove($tablename, $where=null)
	{
		$sql = "delete from $tablename  where $where";
		$res = $this->pdo->exec($sql);
		return $res;
	}


	/**
	 * 修改数据
	 * 参数：表名 要修改的数据的数组
	 * 返回：布尔
	 */
	public function update($tablename, $arr, $where)
	{
//处理传过来的数组
		$str = "";
		foreach ($arr as $k => $v) {
			$str .= "$k='" . $v . "',";
		}
//截取字符串
		$str = substr($str, 0, -1);
//拼接sql语句
		$sql = "update $tablename set $str where $where";
		$res = $this->pdo->exec($sql);
		return $res;
	}




	//MyPDO - 2.0 事务支持

	/**
	 * 执行数据库事务
	 * @access public
	 * @param callable $callback 数据操作方法回调
	 * @return mixed
	 * @throws PDOException
	 * @throws \Exception
	 * @throws \Throwable
	 */
	public function transaction($callback)
	{
		$this->startTrans();
		try {
			$result = null;
			if (is_callable($callback)) {
				$result = call_user_func_array($callback, [$this]);
			}
			$this->commit();
			return $result;
		} catch (\Exception $e) {
			$this->rollback();
			throw $e;
		} catch (\Throwable $e) {
			$this->rollback();
			throw $e;
		}
	}

	/**
	 * 启动事务
	 * @access public
	 * @return bool|mixed
	 * @throws \Exception
	 */
	public function startTrans()
	{
		if (!$this->pdo) {
			return false;
		}

		++$this->transTimes;
		try {
			if (1 == $this->transTimes) {
				$this->pdo->beginTransaction();
			} elseif ($this->transTimes > 1 && $this->supportSavepoint()) {
				$this->pdo->exec(
					$this->parseSavepoint('trans' . $this->transTimes)
				);
			}

		} catch (\PDOException $e) {
			throw $e;
		} catch (\Exception $e) {
			throw $e;
		} catch (\Error $e) {
			throw $e;
		}
	}

	/**
	 * 用于非自动提交状态下面的查询提交
	 * @access public
	 * @return void
	 * @throws PDOException
	 */
	public function commit()
	{

		if (1 == $this->transTimes) {
			$this->pdo->commit();
		}

		--$this->transTimes;
	}

	/**
	 * 事务回滚
	 * @access public
	 * @return void
	 * @throws PDOException
	 */
	public function rollback()
	{

		if (1 == $this->transTimes) {
			$this->pdo->rollBack();
		} elseif ($this->transTimes > 1 && $this->supportSavepoint()) {
			$this->pdo->exec(
				$this->parseSavepointRollBack('trans' . $this->transTimes)
			);
		}

		$this->transTimes = max(0, $this->transTimes - 1);
	}


	/**
	 * 是否支持事务嵌套
	 * @return bool
	 */
	protected function supportSavepoint()
	{
		return false;
	}
	/**
	 * 生成定义保存点的SQL
	 * @param $name
	 * @return string
	 */
	protected function parseSavepoint($name)
	{
		return 'SAVEPOINT ' . $name;
	}

	/**
	 * 生成回滚到保存点的SQL
	 * @param $name
	 * @return string
	 */
	protected function parseSavepointRollBack($name)
	{
		return 'ROLLBACK TO SAVEPOINT ' . $name;
	}


	/**
	 * 批处理执行SQL语句
	 * 批处理的指令都认为是execute操作
	 * @access public
	 * @param array $sqlArray SQL批处理指令
	 * @return boolean
	 */
	public function batchQuery($sqlArray = [])
	{
		if (!is_array($sqlArray)) {
			return false;
		}
		// 自动启动事务支持
		$this->startTrans();
		try {
			foreach ($sqlArray as $sql) {
				$this->exec_sql($sql);
			}
			// 提交事务
			$this->commit();
		} catch (\Exception $e) {
			$this->rollback();
			throw $e;
		}

		return true;
	}

}
?>
